package agent;

import java.util.Vector;
import ap.*;

/**
 *  
 * Meeting Agent-: responsible for setting up a meeting among a group of people
 *  @ author Francis, Aidan and Jonathan
 */

public class MeetingAgent implements Agent{
/**
 * MeetingAgent class used to hold names addresses and times of meeting
 * along with which times suit which contacts.
 * Once the agent has been set, the toString and fromString methods 
 * are used to send the agent, and recreate the agent once received.
 */
	private Vector contacts;    //storage for the number of contacts(string)
	private Vector addresses;   //storage for the address(es) of the receiver(string)
	private Vector startTimes;  //storage for the posible start times of the meeting(string)
	private Vector finishTimes; //storage for the posible end times of the meeting(string)
	private boolean[][] available;//[Noreceivers][noMeetingTimes]value is set to true if a receievr is availabe at a particular time	
	private String agentType;
	
	public static final char TYPE_DATAGRAM 	  = 'D';
	public static final char TYPE_SOCKET      = 'S';
	
	private static final char FIELD_DELIM     = '|';
	private static final char VALUE_DELIM     = '=';
	
	private static final char FIELD_NAME      = 'N';
	private static final char FIELD_ADDRESS   = 'A';
	
	private static final char FIELD_START     = 'S';
	private static final char FIELD_FINISH    = 'F';
	
	private static final char ELEMENT_BOOL    = 'B';
	
	/**default constructor*/
	public MeetingAgent(){
		contacts = new Vector();
		addresses = new Vector();
		startTimes = new Vector();
		finishTimes = new Vector();	
		this.setType();
	}       
	/**************************************************************************
	 * 
	 **/
	public InfoAgent convertToInfo(){
		InfoAgent infoAgent = new InfoAgent();
		for(int i=0; i<this.contacts.size(); i++){
			infoAgent.addContact((String)this.contacts.elementAt(i), (String)this.addresses.elementAt(i));
		}
		int[] temp = new int[this.startTimes.size()];
	
		for(int i=0; i<this.startTimes.size(); i++){
			for(int j=0; j<this.contacts.size(); j++){
				if(this.available[i][j])
					temp[i]++;						
			}
		}
		int max = 0;
		int maxIndex = 0;
		for(int i=0; i<this.startTimes.size(); i++){
			if(temp[i]> max){
				max = temp[i];
				maxIndex = i;
			}
		}
		infoAgent.setMessage("Meeting is from: " + this.startTimes.elementAt(maxIndex) + " to: " + this.finishTimes.elementAt(maxIndex));
		
		return infoAgent;
	}
	/**************************************************************************
	 * access methods
	 **/
	public void setType(){
		this.agentType = Agent.TYPE_MEETING;
	}
	public int sizeOfContacts(){
		return this.contacts.size();
	}
	public int sizeOfAddresses(){
		return this.addresses.size();
	}
	public int sizeOfStartTimes(){
		return this.startTimes.size();
	}
	public int sizeOfFinishTimes(){
		return this.finishTimes.size();
	}
	
	public String getType(){
		return this.agentType;
	}
	public String getContact(int n){
		return this.contacts.elementAt(n).toString();
	}
	public String getAddresses(int n){
		return this.addresses.elementAt(n).toString();
	}
	public String getStartTimes(int n){
		return this.startTimes.elementAt(n).toString();
	}
	public String getFinishTimes(int n){
		return this.finishTimes.elementAt(n).toString();
	}
	public boolean getAvailability(int n, int m){
		return this.available[n][m];
	}
	/* *
	*Return next unvisited index of the person.
	**/
	//for use in infoAgent
	public void incrementToNextAddress(){ ; }
	public int getNextIndex(){
		for(int i = 0 ; i < contacts.size();i++){
			if(available[i][0]==false){
				return i;
			}
		}
		return -1;
	}
	public int getLastContactIndex(){
		return contacts.size()-1;
	}
	public int getLastTimeIndex(){
		return available[0].length;
	}
	/**************************************************************************
	 *  methods to update data
	 **/
	 /*  add a new contact to the agent's contact vector**/
	public void addContact( String newContact, String newAddress ){
		this.contacts.addElement( newContact );
		this.addresses.addElement( newAddress );
	}
	public void addTime( String newStart, String newFinish ){
		this.startTimes.addElement( newStart );
		this.finishTimes.addElement( newFinish );
	}
	public void addAvailableTime(int contactIndex,int timeIndex){
		available[contactIndex][timeIndex] = true;
	}
	/**************************************************************************
	 * read in all the agents values from a string
	 * the order in which they are read in is important and should 
	 * not be changed without also changing the toString function
	 * 
	 * @param agentString
	 */
	public void fromString( String agentString ){
		int i = 0;
		int j = 0;
		String field, value;
		Tokenizer tokField = new Tokenizer(agentString, FIELD_DELIM);
		
		//first read in agent type
		try {
			this.agentType = tokField.nextToken();
		} catch (NoSuchElementException e) {
			e.printStackTrace();
		}
		//then read in all the agent values
		while( tokField.hasMoreTokens() ){			
			try {
				field = tokField.nextToken();
				value = field.substring(2);
				
				if( field.charAt(0) == FIELD_NAME ){					
					this.contacts.addElement(value);
				}
				else if( field.charAt(0) == FIELD_ADDRESS ){
					this.addresses.addElement(value);
				}
				else if( field.charAt(0) == FIELD_START ){
					this.startTimes.addElement(value);
				}
				else if( field.charAt(0) == FIELD_FINISH ){
					this.finishTimes.addElement(value);
				}
				else if( field.charAt(0) == ELEMENT_BOOL ){
					//if the boolean array has not been created, do so now
					if( available == null )
						this.setAvailability();					
					
					if( i < contacts.size() ){
						if( j < startTimes.size()+1 ){
							if( value.charAt(0) == 'T' )
								this.available[i][j] = true;
							else
								this.available[i][j] = false;
							
							j++;
						}
						else
							j = 0;
							i++;
					}
				}				
			} catch (NoSuchElementException e) {
				e.printStackTrace();
			}
		}
	}
	/**************************************************************************
	 * overwrite the toString method to write out all the agents values 
	 * the order in which they are written out is important and should 
	 * not be changed without also changing the fromString function
	 **/
	public String toString() {
		String agentString = new String();
		
		agentString += this.agentType;  	//	MEETING
		agentString += FIELD_DELIM;			//	|
		
		//first write out all contact names and addresses
		for( int i=0; i<this.contacts.size(); i++ ){
			agentString += FIELD_NAME;		//	N
			agentString += VALUE_DELIM;		//	=			
			agentString += this.contacts.elementAt(i);
			agentString += FIELD_DELIM;		//	|
			
			agentString += FIELD_ADDRESS;	//	A
			agentString += VALUE_DELIM;		//	=
			agentString += this.addresses.elementAt(i);
			agentString += FIELD_DELIM;		//	|
		}
		
		//then write out start and finish times
		for( int i=0; i<this.startTimes.size(); i++ ){
			agentString += FIELD_START;		//	S
			agentString += VALUE_DELIM;		//	=			
			agentString += this.startTimes.elementAt(i);
			agentString += FIELD_DELIM;		//	|
			
			agentString += FIELD_FINISH;	//	F
			agentString += VALUE_DELIM;		//	=
			agentString += this.finishTimes.elementAt(i);
			agentString += FIELD_DELIM;		//	|
		}
		
		//if the boolean array has not been created, do so now
		if( available == null )
			this.setAvailability();
		
		//then write out the availability
		for(int i=0; i<this.contacts.size(); i++){
			for(int j=0; j<this.startTimes.size()+1; j++){
				agentString += ELEMENT_BOOL;	//	B
				agentString += VALUE_DELIM;		//	=
				if( this.available[i][j] == true )
					agentString += "T";
				else
					agentString += "F";
				agentString += FIELD_DELIM;		//	|
			}
		}

		return agentString;
	}
	
	/**************************************************************************
	 * by default all contacts are set to unavailable
	 * and the agent creator is set to available 
	 */
	private void setAvailability(){
		available = new boolean[this.contacts.size()][this.startTimes.size()+1];
		
		for(int i=0; i<this.contacts.size(); i++){
			for(int j=0; j<this.startTimes.size()+1; j++){
				//if( j==0 )
				//	this.available[i][j] = true;
				//else
					this.available[i][j] = false;
			}				
		}
	}
	/**************************************************************************
	 * return the first contact that has not yet received the agent
	 * 
	 * @return valid address without port or empty string if not available
	 **/
	public String getNextAddress(){
		String nextAddress = "";		
		
		/*look through all the contact for the next destination*/
		for(int i=0; i<this.contacts.size(); i++){
			/* if the first element in the contacts section of the availability 
			 * array is false: this agent has not yet visited this contact
			 * get this contacts address*/
			if( this.available[i][0]==false ){
					nextAddress = this.addresses.elementAt(i).toString();
					break;
			}
		}
		if(!nextAddress.equals("")){
			Tokenizer tok = new Tokenizer(nextAddress, ":");
			try{
				nextAddress = tok.nextToken() + ":" + tok.nextToken() + ":";
			}catch(Exception e){
				e.printStackTrace();
			}
		}
		System.err.println("nextAddress is : " + nextAddress);
		return nextAddress;
	}
	
	/**
	 * Gets next receiver's port
	 * 
	 * @return valid port or -1 if not available
	 */
	public int getNextPort(){
		String nextAddress = "";
		String port = "-1";		
		String token;
		
		/*look through all the contact for the next destination*/
		for(int i=0; i<this.contacts.size(); i++){
			System.err.println("Before access");
			System.err.println("this.available[i][0] is : " + this.available[i][0]);
			/* if the first element in the contacts section of the availability 
			 * array is false: this agent has not yet visited this contact
			 * get this contacts address & port*/
			if( this.available[i][0]==false ){
					nextAddress = this.addresses.elementAt(i).toString();
					break;
			}
		}
		/*ensure address is not empty*/
		if(!nextAddress.equals("")){
			Tokenizer tok = new Tokenizer(nextAddress, ":");
			try{
				token = tok.nextToken() + ":" + tok.nextToken() + ":";				
				port = nextAddress.substring(token.length());
			}catch(Exception e){				
				e.printStackTrace();
			}
		}
		System.err.println("nextPort is : " + port);
		return Integer.parseInt(port);
	}
}